package s3v1

import (
	"context"
	"fmt"
	"io"
	"time"

	"gitee.com/Luna-CY/Golang-Project-Template/internal/configuration"
	"gitee.com/Luna-CY/Golang-Project-Template/internal/errors"
	"gitee.com/Luna-CY/Golang-Project-Template/internal/logger"
	"gitee.com/Luna-CY/Golang-Project-Template/internal/util/cryptor"
	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/aws/awserr"
	"github.com/aws/aws-sdk-go/aws/credentials"
	"github.com/aws/aws-sdk-go/aws/session"
	"github.com/aws/aws-sdk-go/service/s3"
)

func New(conf configuration.StorageProvider) (*S3, errors.Error) {
	var config = &aws.Config{
		Region:                  aws.String(conf.S3V1.Region),
		Endpoint:                aws.String(conf.S3V1.Endpoint),
		Credentials:             credentials.NewStaticCredentials(conf.S3V1.AccessKey, conf.S3V1.SecretKey, ""),
		DisableComputeChecksums: aws.Bool(conf.S3V1.DisableComputeChecksums),
		DisableSSL:              aws.Bool(conf.S3V1.DisableSSL),
		S3ForcePathStyle:        aws.Bool(!conf.S3V1.DisablePathStyle),
	}

	session, err := session.NewSession(config)
	if err != nil {
		logger.SugarLogger(logger.WithStack()).Errorf("ISSS_V1.N_EW.311203 create s3v1 config failed: %v", err)

		return nil, errors.ErrorServerInternalError("ISSS_V1.N_EW.212312", "创建S3V1配置失败")
	}

	return &S3{
		config: conf,
		s3:     s3.New(session),
	}, nil
}

type S3 struct {
	config configuration.StorageProvider

	s3 *s3.S3
}

func (cls *S3) GetName() string {
	return cls.config.Name
}

func (cls *S3) GetBucket() string {
	return cls.config.S3V1.Bucket
}

func (cls *S3) PutObject(ctx context.Context, filepath string, _ int64, reader io.Reader) errors.Error {
	var input = &s3.PutObjectInput{
		Bucket: aws.String(cls.config.S3V1.Bucket),
		Key:    aws.String(filepath),
		Body:   aws.ReadSeekCloser(reader),
	}

	_, err := cls.s3.PutObject(input)
	if err != nil {
		logger.SugarLogger(logger.WithStack()).Errorf("ISSS_V1.S_S3.PO_CT.651151 put object failed: %v", err)

		return errors.ErrorServerInternalError("ISSS_V1.S_S3.PO_CT.521212", "上传对象失败")
	}

	return nil
}

func (cls *S3) GetObject(ctx context.Context, filepath string) (io.ReadCloser, errors.Error) {
	var input = &s3.GetObjectInput{
		Bucket: aws.String(cls.config.S3V1.Bucket),
		Key:    aws.String(filepath),
	}

	output, err := cls.s3.GetObjectWithContext(ctx, input)
	if err != nil {
		var ae awserr.Error

		if errors.As(err, &ae) && ae.Code() == s3.ErrCodeNoSuchKey {
			return nil, errors.New(errors.ErrorTypeRecordNotFound, "ISSS_V1.S_S3.GO_CT.820339", "对象不存在")
		}

		logger.SugarLogger(logger.WithStack()).Errorf("ISSS_V1.S_S3.GO_CT.811140 get object failed: %v", err)

		return nil, errors.ErrorServerInternalError("ISSS_V1.S_S3.GO_CT.661245", "获取对象失败")
	}

	return output.Body, nil
}

func (cls *S3) DeleteObject(ctx context.Context, filepath string) errors.Error {
	var input = &s3.DeleteObjectInput{
		Bucket: aws.String(cls.config.S3V1.Bucket),
		Key:    aws.String(filepath),
	}

	_, err := cls.s3.DeleteObjectWithContext(ctx, input)
	if err != nil {
		var ae awserr.Error

		if errors.As(err, &ae) && ae.Code() == s3.ErrCodeNoSuchKey {
			return errors.New(errors.ErrorTypeRecordNotFound, "ISSS_V1.S_S3.DE_CT.1072109", "对象不存在")
		}

		logger.SugarLogger(logger.WithStack()).Errorf("ISSS_V1.S_S3.DE_CT.971132 delete object failed: %v", err)

		return errors.ErrorServerInternalError("ISSS_V1.S_S3.DE_CT.801254", "删除对象失败")
	}

	return nil
}

func (cls *S3) GetObjectAccessedUrl(ctx context.Context, filepath string, timeout time.Duration) (string, errors.Error) {
	if cls.config.Cdn.Enable {
		return fmt.Sprintf("%s/%s", cls.config.Cdn.Domain, filepath), nil
	}

	if cls.config.IsPublic {
		return fmt.Sprintf("%s/%s", cls.config.S3V1.Endpoint, filepath), nil
	}

	var input = &s3.GetObjectInput{
		Bucket: aws.String(cls.config.S3V1.Bucket),
		Key:    aws.String(filepath),
	}

	request, _ := cls.s3.GetObjectRequest(input)
	url, err := request.Presign(timeout)
	if err != nil {
		var ae awserr.Error

		if errors.As(err, &ae) && ae.Code() == s3.ErrCodeNoSuchKey {
			return "", errors.New(errors.ErrorTypeRecordNotFound, "ISSS_V1.S_S3.GOAU_RL.1320700", "对象不存在")
		}

		logger.SugarLogger(logger.WithStack()).Errorf("ISSS_V1.S_S3.GOAU_RL.1221121 generate signed url failed: %v", err)

		return "", errors.ErrorServerInternalError("ISSS_V1.S_S3.GOAU_RL.1084846", "生成与签名访问链接失败")
	}

	return url, nil
}

func (cls *S3) GetObjectHash(ctx context.Context, filepath string) (string, string, string, errors.Error) {
	var input = &s3.GetObjectInput{
		Bucket: aws.String(cls.config.S3V1.Bucket),
		Key:    aws.String(filepath),
	}

	object, rerr := cls.s3.GetObjectWithContext(ctx, input)
	if rerr != nil {
		var ae awserr.Error

		if errors.As(rerr, &ae) && ae.Code() == s3.ErrCodeNoSuchKey {
			return "", "", "", errors.New(errors.ErrorTypeRecordNotFound, "ISSS_V1.S_S3.GOH_SH.1540714", "对象不存在")
		}

		logger.SugarLogger(logger.WithStack()).Errorf("ISSS_V1.S_S3.GOH_SH.1381036 get object failed: %v", rerr)

		return "", "", "", errors.ErrorServerInternalError("ISSS_V1.S_S3.GOH_SH.1401046", "获取对象失败")
	}

	defer object.Body.Close()

	md5, sha1, sha256, err := cryptor.SumFile(object.Body)
	if err != nil {
		return "", "", "", err.Relation(errors.ErrorServerInternalError("ISSS_V1.S_S3.GOH_SH.1451056", "获取对象哈希值失败"))
	}

	return md5, sha1, sha256, nil
}
